package etcd

import (
	"context"
	"errors"
	"fmt"
	"github.com/cihub/seelog"
	clientv3 "go.etcd.io/etcd/client/v3"
	"kyland.com/eagle/lib/config"
	kerror "kyland.com/eagle/lib/error"
	"time"
)

type EtcdClient struct {
	*clientv3.Client
}

func GetKeysApi() (*EtcdClient, error) {
	server, err := config.Config.Get("etcd")
	if err != nil {
		seelog.Errorf("get etcd config failed: %v", err)
		return nil, err
	}
	ep := server.UString("endpoints")
	if len(ep) == 0 {
		msg := errors.New("get etcd config failed")
		seelog.Errorf("get etcd config failed: %v", msg)
		return nil, errors.New("get etcd config failed")
	}

	endPoints := []string{fmt.Sprintf("%s", ep)}

	cli, err := clientv3.New(clientv3.Config{
		Endpoints:   endPoints,
		DialTimeout: 5 * time.Second,
	})

	if err != nil {
		seelog.Errorf("new client v3 failed: %v", err)
		return nil, err
	}

	return &EtcdClient{cli}, nil
}

func (cli *EtcdClient) Get(key string) (string, error) {
	kapi := clientv3.KV(cli.Client)

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	resp, err := kapi.Get(ctx, key, clientv3.WithSort(clientv3.SortByCreateRevision, clientv3.SortAscend))
	if err != nil {
		seelog.Errorf("get from db failed: %v", err)
		return "", err
	}

	if resp == nil || resp.Kvs == nil {
		seelog.Errorf("resp from db nil: %v", err)
		return "", kerror.EtcdGetKeyError
	}

	if len(resp.Kvs) == 0 {
		seelog.Errorf("resp from db nil: %v", err)
		return "", kerror.EtcdKeyNotExistError
	}

	return string(resp.Kvs[0].Value), err

}

// GetBatch 批量获取指定前缀的所有 JSON 数据
func (cli *EtcdClient) GetBatch(prefix string) ([][]byte, error) {
	kapi := clientv3.KV(cli.Client)

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	resp, err := kapi.Get(ctx, prefix, clientv3.WithPrefix())
	if err != nil {
		seelog.Errorf("get from db err: %v", err)
		return nil, err
	}
	var data [][]byte
	for _, kv := range resp.Kvs {
		//var item interface{}
		//err := json.Unmarshal(kv.Value, &item)
		//if err != nil {
		//	return nil, fmt.Errorf("failed to unmarshal etcd data, error:%v", err)
		//}
		data = append(data, kv.Value)
	}
	return data, nil
}

// GetJSON 从指定键中获取 JSON 数据
func (cli *EtcdClient) GetJSON(key string) ([]byte, error) {
	kapi := clientv3.KV(cli.Client)

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	resp, err := kapi.Get(ctx, key)
	if err != nil {
		seelog.Errorf("get from db err: %v", err)
		return nil, err
	}
	if resp.Count == 0 {
		return nil, fmt.Errorf("key %s not found", key)
	}
	return resp.Kvs[0].Value, nil
}

func (cli *EtcdClient) Set(key, value string, expire ...int) error {
	kapi := clientv3.KV(cli.Client)

	var ttl clientv3.LeaseID
	ttl = 0
	if len(expire) > 0 {
		ttl = clientv3.LeaseID(expire[0])
	}

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	_, err := kapi.Put(ctx, key, value, clientv3.WithPrevKV(), clientv3.WithLease(ttl))
	if err != nil {
		seelog.Errorf("put  db err: %v", err)
		return err
	}
	return nil
}
func (cli *EtcdClient) SetEX(key, value string, expire ...int) error {
	return cli.Set(key, value, expire...)
}

// 类似于redis的set，expire其实只用第一个
func (cli *EtcdClient) SetNX(key, value string, expire ...int) error {
	kapi := clientv3.KV(cli.Client)
	var ttl clientv3.LeaseID
	ttl = 0
	if len(expire) > 0 {
		ttl = clientv3.LeaseID(expire[0])
	}

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	_, err := kapi.Put(ctx, key, value, clientv3.WithPrevKV(), clientv3.WithLease(ttl))

	return err
}

func (cli *EtcdClient) Ttl(key string) (int64, error) {
	kapi := clientv3.KV(cli.Client)
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	resp, err := kapi.Get(ctx, key, clientv3.WithSort(clientv3.SortByCreateRevision, clientv3.SortAscend))
	return resp.Kvs[0].Lease, err

}
func (cli *EtcdClient) Expire(key string, expire ...int) error {
	kapi := clientv3.KV(cli.Client)
	var ttl clientv3.LeaseID
	ttl = 0
	if len(expire) > 0 {
		ttl = clientv3.LeaseID(expire[0])
	}
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	_, err := kapi.Put(ctx, key, "", clientv3.WithPrevKV(), clientv3.WithLease(ttl))

	return err
}

func (cli *EtcdClient) Del(key string) error {
	kapi := clientv3.KV(cli.Client)

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	_, err := kapi.Delete(ctx, key, clientv3.WithPrevKV())
	if err != nil {
		seelog.Errorf("delete from db err: %v", err)
		return err
	}
	return nil
}
